
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>Descriptor HowTo Guide &#8212; Python 3.9.6 documentation</title>
    <link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
    
    <script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
    <script type="text/javascript" src="../_static/jquery.js"></script>
    <script type="text/javascript" src="../_static/underscore.js"></script>
    <script type="text/javascript" src="../_static/doctools.js"></script>
    <script type="text/javascript" src="../_static/language_data.js"></script>
    
    <script type="text/javascript" src="../_static/sidebar.js"></script>
    
    <link rel="search" type="application/opensearchdescription+xml"
          title="Search within Python 3.9.6 documentation"
          href="../_static/opensearch.xml"/>
    <link rel="author" title="About these documents" href="../about.html" />
    <link rel="index" title="Index" href="../genindex.html" />
    <link rel="search" title="Search" href="../search.html" />
    <link rel="copyright" title="Copyright" href="../copyright.html" />
    <link rel="next" title="Functional Programming HOWTO" href="functional.html" />
    <link rel="prev" title="Curses Programming with Python" href="curses.html" />
    <link rel="canonical" href="https://docs.python.org/3/howto/descriptor.html" />
    
      
      
    

    
    <style>
      @media only screen {
        table.full-width-table {
            width: 100%;
        }
      }
    </style>

    <link rel="shortcut icon" type="image/png" href="../_static/py.png" />
    
    <script type="text/javascript" src="../_static/copybutton.js"></script>
    
     


  </head><body>
  
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="../py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="functional.html" title="Functional Programming HOWTO"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="curses.html" title="Curses Programming with Python"
             accesskey="P">previous</a> |</li>

    <li><img src="../_static/py.png" alt=""
             style="vertical-align: middle; margin-top: -1px"/></li>
    <li><a href="https://www.python.org/">Python</a> &#187;</li>
    

    <li>
      <a href="../index.html">3.9.6 Documentation</a> &#187;
    </li>

          <li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> &#187;</li>
    <li class="right">
        

    <div class="inline-search" style="display: none" role="search">
        <form class="inline-search" action="../search.html" method="get">
          <input placeholder="Quick search" type="text" name="q" />
          <input type="submit" value="Go" />
          <input type="hidden" name="check_keywords" value="yes" />
          <input type="hidden" name="area" value="default" />
        </form>
    </div>
    <script type="text/javascript">$('.inline-search').show(0);</script>
         |
    </li>

      </ul>
    </div>    

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <section id="descriptor-howto-guide">
<span id="descriptorhowto"></span><h1><a class="toc-backref" href="#id1">Descriptor HowTo Guide</a><a class="headerlink" href="#descriptor-howto-guide" title="Permalink to this headline">¶</a></h1>
<dl class="field-list simple">
<dt class="field-odd">Author</dt>
<dd class="field-odd"><p>Raymond Hettinger</p>
</dd>
<dt class="field-even">Contact</dt>
<dd class="field-even"><p>&lt;python at rcn dot com&gt;</p>
</dd>
</dl>
<div class="contents topic" id="contents">
<p class="topic-title">Contents</p>
<ul class="simple">
<li><p><a class="reference internal" href="#descriptor-howto-guide" id="id1">Descriptor HowTo Guide</a></p>
<ul>
<li><p><a class="reference internal" href="#primer" id="id2">Primer</a></p>
<ul>
<li><p><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant" id="id3">Simple example: A descriptor that returns a constant</a></p></li>
<li><p><a class="reference internal" href="#dynamic-lookups" id="id4">Dynamic lookups</a></p></li>
<li><p><a class="reference internal" href="#managed-attributes" id="id5">Managed attributes</a></p></li>
<li><p><a class="reference internal" href="#customized-names" id="id6">Customized names</a></p></li>
<li><p><a class="reference internal" href="#closing-thoughts" id="id7">Closing thoughts</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#complete-practical-example" id="id8">Complete Practical Example</a></p>
<ul>
<li><p><a class="reference internal" href="#validator-class" id="id9">Validator class</a></p></li>
<li><p><a class="reference internal" href="#custom-validators" id="id10">Custom validators</a></p></li>
<li><p><a class="reference internal" href="#practical-application" id="id11">Practical application</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#technical-tutorial" id="id12">Technical Tutorial</a></p>
<ul>
<li><p><a class="reference internal" href="#abstract" id="id13">Abstract</a></p></li>
<li><p><a class="reference internal" href="#definition-and-introduction" id="id14">Definition and introduction</a></p></li>
<li><p><a class="reference internal" href="#descriptor-protocol" id="id15">Descriptor protocol</a></p></li>
<li><p><a class="reference internal" href="#overview-of-descriptor-invocation" id="id16">Overview of descriptor invocation</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-an-instance" id="id17">Invocation from an instance</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-a-class" id="id18">Invocation from a class</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-super" id="id19">Invocation from super</a></p></li>
<li><p><a class="reference internal" href="#summary-of-invocation-logic" id="id20">Summary of invocation logic</a></p></li>
<li><p><a class="reference internal" href="#automatic-name-notification" id="id21">Automatic name notification</a></p></li>
<li><p><a class="reference internal" href="#orm-example" id="id22">ORM example</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#pure-python-equivalents" id="id23">Pure Python Equivalents</a></p>
<ul>
<li><p><a class="reference internal" href="#properties" id="id24">Properties</a></p></li>
<li><p><a class="reference internal" href="#functions-and-methods" id="id25">Functions and methods</a></p></li>
<li><p><a class="reference internal" href="#kinds-of-methods" id="id26">Kinds of methods</a></p></li>
<li><p><a class="reference internal" href="#static-methods" id="id27">Static methods</a></p></li>
<li><p><a class="reference internal" href="#class-methods" id="id28">Class methods</a></p></li>
<li><p><a class="reference internal" href="#member-objects-and-slots" id="id29">Member objects and __slots__</a></p></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<p><a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">Descriptors</span></a> let objects customize attribute lookup,
storage, and deletion.</p>
<p>This guide has four major sections:</p>
<ol class="arabic simple">
<li><p>The “primer” gives a basic overview, moving gently from simple examples,
adding one feature at a time.  Start here if you’re new to descriptors.</p></li>
<li><p>The second section shows a complete, practical descriptor example.  If you
already know the basics, start there.</p></li>
<li><p>The third section provides a more technical tutorial that goes into the
detailed mechanics of how descriptors work.  Most people don’t need this
level of detail.</p></li>
<li><p>The last section has pure Python equivalents for built-in descriptors that
are written in C.  Read this if you’re curious about how functions turn
into bound methods or about the implementation of common tools like
<a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a>, and
<a class="reference internal" href="../glossary.html#term-slots"><span class="xref std std-term">__slots__</span></a>.</p></li>
</ol>
<section id="primer">
<h2><a class="toc-backref" href="#id2">Primer</a><a class="headerlink" href="#primer" title="Permalink to this headline">¶</a></h2>
<p>In this primer, we start with the most basic possible example and then we’ll
add new capabilities one by one.</p>
<section id="simple-example-a-descriptor-that-returns-a-constant">
<h3><a class="toc-backref" href="#id3">Simple example: A descriptor that returns a constant</a><a class="headerlink" href="#simple-example-a-descriptor-that-returns-a-constant" title="Permalink to this headline">¶</a></h3>
<p>The <code class="xref py py-class docutils literal notranslate"><span class="pre">Ten</span></code> class is a descriptor that always returns the constant <code class="docutils literal notranslate"><span class="pre">10</span></code>
from its <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Ten</span><span class="p">:</span>
    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">return</span> <span class="mi">10</span>
</pre></div>
</div>
<p>To use the descriptor, it must be stored as a class variable in another class:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">A</span><span class="p">:</span>
    <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span>                       <span class="c1"># Regular class attribute</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">Ten</span><span class="p">()</span>                   <span class="c1"># Descriptor instance</span>
</pre></div>
</div>
<p>An interactive session shows the difference between normal attribute lookup
and descriptor lookup:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span>                     <span class="c1"># Make an instance of class A</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="o">.</span><span class="n">x</span>                         <span class="c1"># Normal attribute lookup</span>
<span class="go">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="o">.</span><span class="n">y</span>                         <span class="c1"># Descriptor lookup</span>
<span class="go">10</span>
</pre></div>
</div>
<p>In the <code class="docutils literal notranslate"><span class="pre">a.x</span></code> attribute lookup, the dot operator finds the key <code class="docutils literal notranslate"><span class="pre">x</span></code> and the
value <code class="docutils literal notranslate"><span class="pre">5</span></code> in the class dictionary.  In the <code class="docutils literal notranslate"><span class="pre">a.y</span></code> lookup, the dot operator
finds a descriptor instance, recognized by its <code class="docutils literal notranslate"><span class="pre">__get__</span></code> method, and calls
that method which returns <code class="docutils literal notranslate"><span class="pre">10</span></code>.</p>
<p>Note that the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is not stored in either the class dictionary or the
instance dictionary.  Instead, the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is computed on demand.</p>
<p>This example shows how a simple descriptor works, but it isn’t very useful.
For retrieving constants, normal attribute lookup would be better.</p>
<p>In the next section, we’ll create something more useful, a dynamic lookup.</p>
</section>
<section id="dynamic-lookups">
<h3><a class="toc-backref" href="#id4">Dynamic lookups</a><a class="headerlink" href="#dynamic-lookups" title="Permalink to this headline">¶</a></h3>
<p>Interesting descriptors typically run computations instead of returning
constants:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>

<span class="k">class</span> <span class="nc">DirectorySize</span><span class="p">:</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">dirname</span><span class="p">))</span>

<span class="k">class</span> <span class="nc">Directory</span><span class="p">:</span>

    <span class="n">size</span> <span class="o">=</span> <span class="n">DirectorySize</span><span class="p">()</span>              <span class="c1"># Descriptor instance</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span>          <span class="c1"># Regular instance attribute</span>
</pre></div>
</div>
<p>An interactive session shows that the lookup is dynamic — it computes
different, updated answers each time:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">s</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">&#39;songs&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">&#39;games&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">s</span><span class="o">.</span><span class="n">size</span>                              <span class="c1"># The songs directory has twenty files</span>
<span class="go">20</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span><span class="o">.</span><span class="n">size</span>                              <span class="c1"># The games directory has three files</span>
<span class="go">3</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s1">&#39;games/chess&#39;</span><span class="p">)</span>            <span class="c1"># Delete a game</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span><span class="o">.</span><span class="n">size</span>                              <span class="c1"># File count is automatically updated</span>
<span class="go">2</span>
</pre></div>
</div>
<p>Besides showing how descriptors can run computations, this example also
reveals the purpose of the parameters to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>.  The <em>self</em>
parameter is <em>size</em>, an instance of <em>DirectorySize</em>.  The <em>obj</em> parameter is
either <em>g</em> or <em>s</em>, an instance of <em>Directory</em>.  It is the <em>obj</em> parameter that
lets the <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method learn the target directory.  The <em>objtype</em>
parameter is the class <em>Directory</em>.</p>
</section>
<section id="managed-attributes">
<h3><a class="toc-backref" href="#id5">Managed attributes</a><a class="headerlink" href="#managed-attributes" title="Permalink to this headline">¶</a></h3>
<p>A popular use for descriptors is managing access to instance data.  The
descriptor is assigned to a public attribute in the class dictionary while the
actual data is stored as a private attribute in the instance dictionary.  The
descriptor’s <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> methods are triggered when
the public attribute is accessed.</p>
<p>In the following example, <em>age</em> is the public attribute and <em>_age</em> is the
private attribute.  When the public attribute is accessed, the descriptor logs
the lookup or update:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>

<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">LoggedAgeAccess</span><span class="p">:</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_age</span>
        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;age&#39;</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">value</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;age&#39;</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
        <span class="n">obj</span><span class="o">.</span><span class="n">_age</span> <span class="o">=</span> <span class="n">value</span>

<span class="k">class</span> <span class="nc">Person</span><span class="p">:</span>

    <span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAgeAccess</span><span class="p">()</span>             <span class="c1"># Descriptor instance</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>                <span class="c1"># Regular instance attribute</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>                  <span class="c1"># Calls __set__()</span>

    <span class="k">def</span> <span class="nf">birthday</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">+=</span> <span class="mi">1</span>                   <span class="c1"># Calls both __get__() and __set__()</span>
</pre></div>
</div>
<p>An interactive session shows that all access to the managed attribute <em>age</em> is
logged, but that the regular attribute <em>name</em> is not logged:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Mary M&#39;</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span>         <span class="c1"># The initial age update is logged</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 30</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;David D&#39;</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 40</span>

<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">mary</span><span class="p">)</span>                          <span class="c1"># The actual data is in a private attribute</span>
<span class="go">{&#39;name&#39;: &#39;Mary M&#39;, &#39;_age&#39;: 30}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">dave</span><span class="p">)</span>
<span class="go">{&#39;name&#39;: &#39;David D&#39;, &#39;_age&#39;: 40}</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span><span class="o">.</span><span class="n">age</span>                            <span class="c1"># Access the data and log the lookup</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 30</span>
<span class="go">30</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span><span class="o">.</span><span class="n">birthday</span><span class="p">()</span>                     <span class="c1"># Updates are logged as well</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 30</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 31</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span><span class="o">.</span><span class="n">name</span>                           <span class="c1"># Regular attribute lookup isn&#39;t logged</span>
<span class="go">&#39;David D&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span><span class="o">.</span><span class="n">age</span>                            <span class="c1"># Only the managed attribute is logged</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 40</span>
<span class="go">40</span>
</pre></div>
</div>
<p>One major issue with this example is that the private name <em>_age</em> is hardwired in
the <em>LoggedAgeAccess</em> class.  That means that each instance can only have one
logged attribute and that its name is unchangeable.  In the next example,
we’ll fix that problem.</p>
</section>
<section id="customized-names">
<h3><a class="toc-backref" href="#id6">Customized names</a><a class="headerlink" href="#customized-names" title="Permalink to this headline">¶</a></h3>
<p>When a class uses descriptors, it can inform each descriptor about which
variable name was used.</p>
<p>In this example, the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has two descriptor instances,
<em>name</em> and <em>age</em>.  When the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class is defined, it makes a
callback to <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> in <em>LoggedAccess</em> so that the field names can
be recorded, giving each descriptor its own <em>public_name</em> and <em>private_name</em>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>

<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">LoggedAccess</span><span class="p">:</span>

    <span class="k">def</span> <span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span> <span class="o">=</span> <span class="n">name</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">&#39;_&#39;</span> <span class="o">+</span> <span class="n">name</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">value</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
        <span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">Person</span><span class="p">:</span>

    <span class="n">name</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span>                <span class="c1"># First descriptor instance</span>
    <span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span>                 <span class="c1"># Second descriptor instance</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>                 <span class="c1"># Calls the first descriptor</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>                   <span class="c1"># Calls the second descriptor</span>

    <span class="k">def</span> <span class="nf">birthday</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>An interactive session shows that the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has called
<a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> so that the field names would be recorded.  Here
we call <a class="reference internal" href="../library/functions.html#vars" title="vars"><code class="xref py py-func docutils literal notranslate"><span class="pre">vars()</span></code></a> to look up the descriptor without triggering it:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">&#39;name&#39;</span><span class="p">])</span>
<span class="go">{&#39;public_name&#39;: &#39;name&#39;, &#39;private_name&#39;: &#39;_name&#39;}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">&#39;age&#39;</span><span class="p">])</span>
<span class="go">{&#39;public_name&#39;: &#39;age&#39;, &#39;private_name&#39;: &#39;_age&#39;}</span>
</pre></div>
</div>
<p>The new class now logs access to both <em>name</em> and <em>age</em>:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">pete</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Peter P&#39;</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;name&#39; to &#39;Peter P&#39;</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 10</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">kate</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Catherine C&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;name&#39; to &#39;Catherine C&#39;</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 20</span>
</pre></div>
</div>
<p>The two <em>Person</em> instances contain only the private names:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">pete</span><span class="p">)</span>
<span class="go">{&#39;_name&#39;: &#39;Peter P&#39;, &#39;_age&#39;: 10}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">kate</span><span class="p">)</span>
<span class="go">{&#39;_name&#39;: &#39;Catherine C&#39;, &#39;_age&#39;: 20}</span>
</pre></div>
</div>
</section>
<section id="closing-thoughts">
<h3><a class="toc-backref" href="#id7">Closing thoughts</a><a class="headerlink" href="#closing-thoughts" title="Permalink to this headline">¶</a></h3>
<p>A <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a> is what we call any object that defines <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>,
<a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>, or <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>.</p>
<p>Optionally, descriptors can have a <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> method.  This is only
used in cases where a descriptor needs to know either the class where it was
created or the name of class variable it was assigned to.  (This method, if
present, is called even if the class is not a descriptor.)</p>
<p>Descriptors get invoked by the dot “operator” during attribute lookup.  If a
descriptor is accessed indirectly with <code class="docutils literal notranslate"><span class="pre">vars(some_class)[descriptor_name]</span></code>,
the descriptor instance is returned without invoking it.</p>
<p>Descriptors only work when used as class variables.  When put in instances,
they have no effect.</p>
<p>The main motivation for descriptors is to provide a hook allowing objects
stored in class variables to control what happens during attribute lookup.</p>
<p>Traditionally, the calling class controls what happens during lookup.
Descriptors invert that relationship and allow the data being looked-up to
have a say in the matter.</p>
<p>Descriptors are used throughout the language.  It is how functions turn into
bound methods.  Common tools like <a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a>, <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a>,
<a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a>, and <a class="reference internal" href="../library/functools.html#functools.cached_property" title="functools.cached_property"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.cached_property()</span></code></a> are all implemented as
descriptors.</p>
</section>
</section>
<section id="complete-practical-example">
<h2><a class="toc-backref" href="#id8">Complete Practical Example</a><a class="headerlink" href="#complete-practical-example" title="Permalink to this headline">¶</a></h2>
<p>In this example, we create a practical and powerful tool for locating
notoriously hard to find data corruption bugs.</p>
<section id="validator-class">
<h3><a class="toc-backref" href="#id9">Validator class</a><a class="headerlink" href="#validator-class" title="Permalink to this headline">¶</a></h3>
<p>A validator is a descriptor for managed attribute access.  Prior to storing
any data, it verifies that the new value meets various type and range
restrictions.  If those restrictions aren’t met, it raises an exception to
prevent data corruption at its source.</p>
<p>This <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> class is both an <a class="reference internal" href="../glossary.html#term-abstract-base-class"><span class="xref std std-term">abstract base class</span></a> and a
managed attribute descriptor:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>

<span class="k">class</span> <span class="nc">Validator</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">&#39;_&#39;</span> <span class="o">+</span> <span class="n">name</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>

    <span class="nd">@abstractmethod</span>
    <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">pass</span>
</pre></div>
</div>
<p>Custom validators need to inherit from <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> and must supply a
<code class="xref py py-meth docutils literal notranslate"><span class="pre">validate()</span></code> method to test various restrictions as needed.</p>
</section>
<section id="custom-validators">
<h3><a class="toc-backref" href="#id10">Custom validators</a><a class="headerlink" href="#custom-validators" title="Permalink to this headline">¶</a></h3>
<p>Here are three practical data validation utilities:</p>
<ol class="arabic simple">
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">OneOf</span></code> verifies that a value is one of a restricted set of options.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">Number</span></code> verifies that a value is either an <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a> or
<a class="reference internal" href="../library/functions.html#float" title="float"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>.  Optionally, it verifies that a value is between a given
minimum or maximum.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">String</span></code> verifies that a value is a <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a>.  Optionally, it
validates a given minimum or maximum length.  It can validate a
user-defined <a class="reference external" href="https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)">predicate</a> as well.</p></li>
</ol>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">OneOf</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">value</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be one of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="si">!r}</span><span class="s1">&#39;</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">Number</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minvalue</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxvalue</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span> <span class="o">=</span> <span class="n">minvalue</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="o">=</span> <span class="n">maxvalue</span>

    <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">)):</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be an int or float&#39;</span><span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be at least </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no more than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>

<span class="k">class</span> <span class="nc">String</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">predicate</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span> <span class="o">=</span> <span class="n">minsize</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="o">=</span> <span class="n">maxsize</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="o">=</span> <span class="n">predicate</span>

    <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be an str&#39;</span><span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no smaller than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&gt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no bigger than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="si">}</span><span class="s1"> to be true for </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
</pre></div>
</div>
</section>
<section id="practical-application">
<h3><a class="toc-backref" href="#id11">Practical application</a><a class="headerlink" href="#practical-application" title="Permalink to this headline">¶</a></h3>
<p>Here’s how the data validators can be used in a real class:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Component</span><span class="p">:</span>

    <span class="n">name</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="n">minsize</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">predicate</span><span class="o">=</span><span class="nb">str</span><span class="o">.</span><span class="n">isupper</span><span class="p">)</span>
    <span class="n">kind</span> <span class="o">=</span> <span class="n">OneOf</span><span class="p">(</span><span class="s1">&#39;wood&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="s1">&#39;plastic&#39;</span><span class="p">)</span>
    <span class="n">quantity</span> <span class="o">=</span> <span class="n">Number</span><span class="p">(</span><span class="n">minvalue</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">kind</span> <span class="o">=</span> <span class="n">kind</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">quantity</span> <span class="o">=</span> <span class="n">quantity</span>
</pre></div>
</div>
<p>The descriptors prevent invalid instances from being created:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;Widget&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>      <span class="c1"># Blocked: &#39;Widget&#39; is not all uppercase</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected &lt;method &#39;isupper&#39; of &#39;str&#39; objects&gt; to be true for &#39;Widget&#39;</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metle&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>      <span class="c1"># Blocked: &#39;metle&#39; is misspelled</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected &#39;metle&#39; to be one of {&#39;metal&#39;, &#39;plastic&#39;, &#39;wood&#39;}</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">5</span><span class="p">)</span>     <span class="c1"># Blocked: -5 is negative</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected -5 to be at least 0</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="s1">&#39;V&#39;</span><span class="p">)</span>    <span class="c1"># Blocked: &#39;V&#39; isn&#39;t a number</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">TypeError</span>: <span class="n">Expected &#39;V&#39; to be an int or float</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span> <span class="o">=</span> <span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>  <span class="c1"># Allowed:  The inputs are valid</span>
</pre></div>
</div>
</section>
</section>
<section id="technical-tutorial">
<h2><a class="toc-backref" href="#id12">Technical Tutorial</a><a class="headerlink" href="#technical-tutorial" title="Permalink to this headline">¶</a></h2>
<p>What follows is a more technical tutorial for the mechanics and details of how
descriptors work.</p>
<section id="abstract">
<h3><a class="toc-backref" href="#id13">Abstract</a><a class="headerlink" href="#abstract" title="Permalink to this headline">¶</a></h3>
<p>Defines descriptors, summarizes the protocol, and shows how descriptors are
called.  Provides an example showing how object relational mappings work.</p>
<p>Learning about descriptors not only provides access to a larger toolset, it
creates a deeper understanding of how Python works.</p>
</section>
<section id="definition-and-introduction">
<h3><a class="toc-backref" href="#id14">Definition and introduction</a><a class="headerlink" href="#definition-and-introduction" title="Permalink to this headline">¶</a></h3>
<p>In general, a descriptor is an attribute value that has one of the methods in
the descriptor protocol.  Those methods are <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>, <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>,
and <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>.  If any of those methods are defined for an
attribute, it is said to be a <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a>.</p>
<p>The default behavior for attribute access is to get, set, or delete the
attribute from an object’s dictionary.  For instance, <code class="docutils literal notranslate"><span class="pre">a.x</span></code> has a lookup chain
starting with <code class="docutils literal notranslate"><span class="pre">a.__dict__['x']</span></code>, then <code class="docutils literal notranslate"><span class="pre">type(a).__dict__['x']</span></code>, and
continuing through the method resolution order of <code class="docutils literal notranslate"><span class="pre">type(a)</span></code>. If the
looked-up value is an object defining one of the descriptor methods, then Python
may override the default behavior and invoke the descriptor method instead.
Where this occurs in the precedence chain depends on which descriptor methods
were defined.</p>
<p>Descriptors are a powerful, general purpose protocol.  They are the mechanism
behind properties, methods, static methods, class methods, and
<a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.  They are used throughout Python itself.  Descriptors
simplify the underlying C code and offer a flexible set of new tools for
everyday Python programs.</p>
</section>
<section id="descriptor-protocol">
<h3><a class="toc-backref" href="#id15">Descriptor protocol</a><a class="headerlink" href="#descriptor-protocol" title="Permalink to this headline">¶</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">descr.__get__(self,</span> <span class="pre">obj,</span> <span class="pre">type=None)</span> <span class="pre">-&gt;</span> <span class="pre">value</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__set__(self,</span> <span class="pre">obj,</span> <span class="pre">value)</span> <span class="pre">-&gt;</span> <span class="pre">None</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</span> <span class="pre">-&gt;</span> <span class="pre">None</span></code></p>
<p>That is all there is to it.  Define any of these methods and an object is
considered a descriptor and can override default behavior upon being looked up
as an attribute.</p>
<p>If an object defines <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> or <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></a>, it is considered
a data descriptor.  Descriptors that only define <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> are called
non-data descriptors (they are often used for methods but other uses are
possible).</p>
<p>Data and non-data descriptors differ in how overrides are calculated with
respect to entries in an instance’s dictionary.  If an instance’s dictionary
has an entry with the same name as a data descriptor, the data descriptor
takes precedence.  If an instance’s dictionary has an entry with the same
name as a non-data descriptor, the dictionary entry takes precedence.</p>
<p>To make a read-only data descriptor, define both <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and
<a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> with the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> raising an <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a> when
called.  Defining the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> method with an exception raising
placeholder is enough to make it a data descriptor.</p>
</section>
<section id="overview-of-descriptor-invocation">
<h3><a class="toc-backref" href="#id16">Overview of descriptor invocation</a><a class="headerlink" href="#overview-of-descriptor-invocation" title="Permalink to this headline">¶</a></h3>
<p>A descriptor can be called directly with <code class="docutils literal notranslate"><span class="pre">desc.__get__(obj)</span></code> or
<code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">cls)</span></code>.</p>
<p>But it is more common for a descriptor to be invoked automatically from
attribute access.</p>
<p>The expression <code class="docutils literal notranslate"><span class="pre">obj.x</span></code> looks up the attribute <code class="docutils literal notranslate"><span class="pre">x</span></code> in the chain of
namespaces for <code class="docutils literal notranslate"><span class="pre">obj</span></code>.  If the search finds a descriptor outside of the
instance <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, its <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method is invoked according to the
precedence rules listed below.</p>
<p>The details of invocation depend on whether <code class="docutils literal notranslate"><span class="pre">obj</span></code> is an object, class, or
instance of super.</p>
</section>
<section id="invocation-from-an-instance">
<h3><a class="toc-backref" href="#id17">Invocation from an instance</a><a class="headerlink" href="#invocation-from-an-instance" title="Permalink to this headline">¶</a></h3>
<p>Instance lookup scans through a chain of namespaces giving data descriptors
the highest priority, followed by instance variables, then non-data
descriptors, then class variables, and lastly <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> if it is
provided.</p>
<p>If a descriptor is found for <code class="docutils literal notranslate"><span class="pre">a.x</span></code>, then it is invoked with:
<code class="docutils literal notranslate"><span class="pre">desc.__get__(a,</span> <span class="pre">type(a))</span></code>.</p>
<p>The logic for a dotted lookup is in <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a>.  Here is
a pure Python equivalent:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">object_getattribute</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
    <span class="s2">&quot;Emulate PyObject_GenericGetAttr() in Objects/object.c&quot;</span>
    <span class="n">null</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
    <span class="n">objtype</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
    <span class="n">cls_var</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">objtype</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
    <span class="n">descr_get</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__get__&#39;</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
        <span class="k">if</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__set__&#39;</span><span class="p">)</span>
            <span class="ow">or</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__delete__&#39;</span><span class="p">)):</span>
            <span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="p">)</span>     <span class="c1"># data descriptor</span>
    <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s1">&#39;__dict__&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">)[</span><span class="n">name</span><span class="p">]</span>                          <span class="c1"># instance variable</span>
    <span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="p">)</span>         <span class="c1"># non-data descriptor</span>
    <span class="k">if</span> <span class="n">cls_var</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">cls_var</span>                                  <span class="c1"># class variable</span>
    <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
<p>Interestingly, attribute lookup doesn’t call <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a>
directly.  Instead, both the dot operator and the <a class="reference internal" href="../library/functions.html#getattr" title="getattr"><code class="xref py py-func docutils literal notranslate"><span class="pre">getattr()</span></code></a> function
perform attribute lookup by way of a helper function:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getattr_hook</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
    <span class="s2">&quot;Emulate slot_tp_getattr_hook() in Objects/typeobject.c&quot;</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="fm">__getattribute__</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">),</span> <span class="s1">&#39;__getattr__&#39;</span><span class="p">):</span>
            <span class="k">raise</span>
    <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getattr__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>             <span class="c1"># __getattr__</span>
</pre></div>
</div>
<p>So if <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> exists, it is called whenever <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
raises <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a> (either directly or in one of the descriptor calls).</p>
<p>Also, if a user calls <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> directly, the
<a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> hook is bypassed entirely.</p>
</section>
<section id="invocation-from-a-class">
<h3><a class="toc-backref" href="#id18">Invocation from a class</a><a class="headerlink" href="#invocation-from-a-class" title="Permalink to this headline">¶</a></h3>
<p>The logic for a dotted lookup such as <code class="docutils literal notranslate"><span class="pre">A.x</span></code> is in
<code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code>.  The steps are similar to those for
<a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> but the instance dictionary lookup is replaced
by a search through the class’s <a class="reference internal" href="../glossary.html#term-method-resolution-order"><span class="xref std std-term">method resolution order</span></a>.</p>
<p>If a descriptor is found, it is invoked with <code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">A)</span></code>.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_getattro()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">_PyType_Lookup()</span></code> in <a class="reference external" href="https://github.com/python/cpython/tree/3.9/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
</section>
<section id="invocation-from-super">
<h3><a class="toc-backref" href="#id19">Invocation from super</a><a class="headerlink" href="#invocation-from-super" title="Permalink to this headline">¶</a></h3>
<p>The logic for super’s dotted lookup is in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> method for
object returned by <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-class docutils literal notranslate"><span class="pre">super()</span></code></a>.</p>
<p>A dotted lookup such as <code class="docutils literal notranslate"><span class="pre">super(A,</span> <span class="pre">obj).m</span></code> searches <code class="docutils literal notranslate"><span class="pre">obj.__class__.__mro__</span></code>
for the base class <code class="docutils literal notranslate"><span class="pre">B</span></code> immediately following <code class="docutils literal notranslate"><span class="pre">A</span></code> and then returns
<code class="docutils literal notranslate"><span class="pre">B.__dict__['m'].__get__(obj,</span> <span class="pre">A)</span></code>.  If not a descriptor, <code class="docutils literal notranslate"><span class="pre">m</span></code> is returned
unchanged.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">super_getattro()</span></code> in
<a class="reference external" href="https://github.com/python/cpython/tree/3.9/Objects/typeobject.c">Objects/typeobject.c</a>.  A pure Python equivalent can be found in
<a class="reference external" href="https://www.python.org/download/releases/2.2.3/descrintro/#cooperation">Guido’s Tutorial</a>.</p>
</section>
<section id="summary-of-invocation-logic">
<h3><a class="toc-backref" href="#id20">Summary of invocation logic</a><a class="headerlink" href="#summary-of-invocation-logic" title="Permalink to this headline">¶</a></h3>
<p>The mechanism for descriptors is embedded in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
methods for <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a>, and <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.</p>
<p>The important points to remember are:</p>
<ul class="simple">
<li><p>Descriptors are invoked by the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> method.</p></li>
<li><p>Classes inherit this machinery from <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a>, or
<a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>.</p></li>
<li><p>Overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> prevents automatic descriptor calls
because all the descriptor logic is in that method.</p></li>
<li><p><a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> and <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> make
different calls to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>.  The first includes the instance and may
include the class.  The second puts in <code class="docutils literal notranslate"><span class="pre">None</span></code> for the instance and always
includes the class.</p></li>
<li><p>Data descriptors always override instance dictionaries.</p></li>
<li><p>Non-data descriptors may be overridden by instance dictionaries.</p></li>
</ul>
</section>
<section id="automatic-name-notification">
<h3><a class="toc-backref" href="#id21">Automatic name notification</a><a class="headerlink" href="#automatic-name-notification" title="Permalink to this headline">¶</a></h3>
<p>Sometimes it is desirable for a descriptor to know what class variable name it
was assigned to.  When a new class is created, the <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a> metaclass
scans the dictionary of the new class.  If any of the entries are descriptors
and if they define <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a>, that method is called with two
arguments.  The <em>owner</em> is the class where the descriptor is used, and the
<em>name</em> is the class variable the descriptor was assigned to.</p>
<p>The implementation details are in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_new()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">set_names()</span></code> in <a class="reference external" href="https://github.com/python/cpython/tree/3.9/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
<p>Since the update logic is in <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__new__()</span></code>, notifications only take
place at the time of class creation.  If descriptors are added to the class
afterwards, <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> will need to be called manually.</p>
</section>
<section id="orm-example">
<h3><a class="toc-backref" href="#id22">ORM example</a><a class="headerlink" href="#orm-example" title="Permalink to this headline">¶</a></h3>
<p>The following code is simplified skeleton showing how data descriptors could
be used to implement an <a class="reference external" href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping">object relational mapping</a>.</p>
<p>The essential idea is that the data is stored in an external database.  The
Python instances only hold keys to the database’s tables.  Descriptors take
care of lookups or updates:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Field</span><span class="p">:</span>

    <span class="k">def</span> <span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fetch</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;SELECT </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1"> FROM </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;&#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;UPDATE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> SET </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">=? WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;&#39;</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fetch</span><span class="p">,</span> <span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="p">[</span><span class="n">value</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span>
        <span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
</div>
<p>We can use the <code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code> class to define <a class="reference external" href="https://en.wikipedia.org/wiki/Database_model">models</a> that describe the schema for
each table in a database:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Movie</span><span class="p">:</span>
    <span class="n">table</span> <span class="o">=</span> <span class="s1">&#39;Movies&#39;</span>                    <span class="c1"># Table name</span>
    <span class="n">key</span> <span class="o">=</span> <span class="s1">&#39;title&#39;</span>                       <span class="c1"># Primary key</span>
    <span class="n">director</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
    <span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>

<span class="k">class</span> <span class="nc">Song</span><span class="p">:</span>
    <span class="n">table</span> <span class="o">=</span> <span class="s1">&#39;Music&#39;</span>
    <span class="n">key</span> <span class="o">=</span> <span class="s1">&#39;title&#39;</span>
    <span class="n">artist</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
    <span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
    <span class="n">genre</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
</pre></div>
</div>
<p>To use the models, first connect to the database:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">sqlite3</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;entertainment.db&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>An interactive session shows how data is retrieved from the database and how
it can be updated:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">&#39;George Lucas&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">jaws</span> <span class="o">=</span> <span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Jaws&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="sa">f</span><span class="s1">&#39;Released in </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">year</span><span class="si">}</span><span class="s1"> by </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">director</span><span class="si">}</span><span class="s1">&#39;</span>
<span class="go">&#39;Released in 1975 by Steven Spielberg&#39;</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">Song</span><span class="p">(</span><span class="s1">&#39;Country Roads&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">artist</span>
<span class="go">&#39;John Denver&#39;</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span> <span class="o">=</span> <span class="s1">&#39;J.J. Abrams&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">&#39;J.J. Abrams&#39;</span>
</pre></div>
</div>
</section>
</section>
<section id="pure-python-equivalents">
<h2><a class="toc-backref" href="#id23">Pure Python Equivalents</a><a class="headerlink" href="#pure-python-equivalents" title="Permalink to this headline">¶</a></h2>
<p>The descriptor protocol is simple and offers exciting possibilities.  Several
use cases are so common that they have been prepackaged into built-in tools.
Properties, bound methods, static methods, class methods, and __slots__ are
all based on the descriptor protocol.</p>
<section id="properties">
<h3><a class="toc-backref" href="#id24">Properties</a><a class="headerlink" href="#properties" title="Permalink to this headline">¶</a></h3>
<p>Calling <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> is a succinct way of building a data descriptor that
triggers a function call upon access to an attribute.  Its signature is:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">property</span><span class="p">(</span><span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">property</span>
</pre></div>
</div>
<p>The documentation shows a typical use to define a managed attribute <code class="docutils literal notranslate"><span class="pre">x</span></code>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">getx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
    <span class="k">def</span> <span class="nf">setx</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="o">=</span> <span class="n">value</span>
    <span class="k">def</span> <span class="nf">delx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span>
    <span class="n">x</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getx</span><span class="p">,</span> <span class="n">setx</span><span class="p">,</span> <span class="n">delx</span><span class="p">,</span> <span class="s2">&quot;I&#39;m the &#39;x&#39; property.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>To see how <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> is implemented in terms of the descriptor protocol,
here is a pure Python equivalent:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Property</span><span class="p">:</span>
    <span class="s2">&quot;Emulate PyProperty_Type() in Objects/descrobject.c&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="o">=</span> <span class="n">fget</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="o">=</span> <span class="n">fset</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="o">=</span> <span class="n">fdel</span>
        <span class="k">if</span> <span class="n">doc</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">fget</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
            <span class="n">doc</span> <span class="o">=</span> <span class="n">fget</span><span class="o">.</span><span class="vm">__doc__</span>
        <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span> <span class="o">=</span> <span class="n">doc</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">self</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;unreadable attribute&quot;</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;can&#39;t set attribute&quot;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">&quot;can&#39;t delete attribute&quot;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">getter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">setter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fset</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">deleter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fdel</span><span class="p">):</span>
        <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> builtin helps whenever a user interface has granted
attribute access and then subsequent changes require the intervention of a
method.</p>
<p>For instance, a spreadsheet class may grant access to a cell value through
<code class="docutils literal notranslate"><span class="pre">Cell('b10').value</span></code>. Subsequent improvements to the program require the cell
to be recalculated on every access; however, the programmer does not want to
affect existing client code accessing the attribute directly.  The solution is
to wrap access to the value attribute in a property data descriptor:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Cell</span><span class="p">:</span>
    <span class="o">...</span>

    <span class="nd">@property</span>
    <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="s2">&quot;Recalculate the cell before returning value&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">recalc</span><span class="p">()</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_value</span>
</pre></div>
</div>
<p>Either the built-in <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> or our <code class="xref py py-func docutils literal notranslate"><span class="pre">Property()</span></code> equivalent would
work in this example.</p>
</section>
<section id="functions-and-methods">
<h3><a class="toc-backref" href="#id25">Functions and methods</a><a class="headerlink" href="#functions-and-methods" title="Permalink to this headline">¶</a></h3>
<p>Python’s object oriented features are built upon a function based environment.
Using non-data descriptors, the two are merged seamlessly.</p>
<p>Functions stored in class dictionaries get turned into methods when invoked.
Methods only differ from regular functions in that the object instance is
prepended to the other arguments.  By convention, the instance is called
<em>self</em> but could be called <em>this</em> or any other variable name.</p>
<p>Methods can be created manually with <a class="reference internal" href="../library/types.html#types.MethodType" title="types.MethodType"><code class="xref py py-class docutils literal notranslate"><span class="pre">types.MethodType</span></code></a> which is
roughly equivalent to:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MethodType</span><span class="p">:</span>
    <span class="s2">&quot;Emulate PyMethod_Type in Objects/classobject.c&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span> <span class="o">=</span> <span class="n">func</span>
        <span class="bp">self</span><span class="o">.</span><span class="vm">__self__</span> <span class="o">=</span> <span class="n">obj</span>

    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span>
        <span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__self__</span>
        <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>To support automatic creation of methods, functions include the
<a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method for binding methods during attribute access.  This
means that functions are non-data descriptors that return bound methods
during dotted lookup from an instance.  Here’s how it works:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Function</span><span class="p">:</span>
    <span class="o">...</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="s2">&quot;Simulate func_descr_get() in Objects/funcobject.c&quot;</span>
        <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">self</span>
        <span class="k">return</span> <span class="n">MethodType</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
</pre></div>
</div>
<p>Running the following class in the interpreter shows how the function
descriptor works in practice:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">D</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
         <span class="k">return</span> <span class="n">x</span>
</pre></div>
</div>
<p>The function has a <a class="reference internal" href="../glossary.html#term-qualified-name"><span class="xref std std-term">qualified name</span></a> attribute to support introspection:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__qualname__</span>
<span class="go">&#39;D.f&#39;</span>
</pre></div>
</div>
<p>Accessing the function through the class dictionary does not invoke
<a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>.  Instead, it just returns the underlying function object:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="s1">&#39;f&#39;</span><span class="p">]</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
</pre></div>
</div>
<p>Dotted access from a class calls <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> which just returns the
underlying function unchanged:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
</pre></div>
</div>
<p>The interesting behavior occurs during dotted access from an instance.  The
dotted lookup calls <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> which returns a bound method object:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="n">D</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;bound method D.f of &lt;__main__.D object at 0x00B18C90&gt;&gt;</span>
</pre></div>
</div>
<p>Internally, the bound method stores the underlying function and the bound
instance:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__func__</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__self__</span>
<span class="go">&lt;__main__.D object at 0x1012e1f98&gt;</span>
</pre></div>
</div>
<p>If you have ever wondered where <em>self</em> comes from in regular methods or where
<em>cls</em> comes from in class methods, this is it!</p>
</section>
<section id="kinds-of-methods">
<h3><a class="toc-backref" href="#id26">Kinds of methods</a><a class="headerlink" href="#kinds-of-methods" title="Permalink to this headline">¶</a></h3>
<p>Non-data descriptors provide a simple mechanism for variations on the usual
patterns of binding functions into methods.</p>
<p>To recap, functions have a <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method so that they can be converted
to a method when accessed as attributes.  The non-data descriptor transforms an
<code class="docutils literal notranslate"><span class="pre">obj.f(*args)</span></code> call into <code class="docutils literal notranslate"><span class="pre">f(obj,</span> <span class="pre">*args)</span></code>.  Calling <code class="docutils literal notranslate"><span class="pre">cls.f(*args)</span></code>
becomes <code class="docutils literal notranslate"><span class="pre">f(*args)</span></code>.</p>
<p>This chart summarizes the binding and its two most useful variants:</p>
<blockquote>
<div><table class="docutils align-default">
<colgroup>
<col style="width: 30%" />
<col style="width: 39%" />
<col style="width: 32%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Transformation</p></th>
<th class="head"><p>Called from an
object</p></th>
<th class="head"><p>Called from a
class</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>function</p></td>
<td><p>f(obj, *args)</p></td>
<td><p>f(*args)</p></td>
</tr>
<tr class="row-odd"><td><p>staticmethod</p></td>
<td><p>f(*args)</p></td>
<td><p>f(*args)</p></td>
</tr>
<tr class="row-even"><td><p>classmethod</p></td>
<td><p>f(type(obj), *args)</p></td>
<td><p>f(cls, *args)</p></td>
</tr>
</tbody>
</table>
</div></blockquote>
</section>
<section id="static-methods">
<h3><a class="toc-backref" href="#id27">Static methods</a><a class="headerlink" href="#static-methods" title="Permalink to this headline">¶</a></h3>
<p>Static methods return the underlying function without changes.  Calling either
<code class="docutils literal notranslate"><span class="pre">c.f</span></code> or <code class="docutils literal notranslate"><span class="pre">C.f</span></code> is the equivalent of a direct lookup into
<code class="docutils literal notranslate"><span class="pre">object.__getattribute__(c,</span> <span class="pre">&quot;f&quot;)</span></code> or <code class="docutils literal notranslate"><span class="pre">object.__getattribute__(C,</span> <span class="pre">&quot;f&quot;)</span></code>. As a
result, the function becomes identically accessible from either an object or a
class.</p>
<p>Good candidates for static methods are methods that do not reference the
<code class="docutils literal notranslate"><span class="pre">self</span></code> variable.</p>
<p>For instance, a statistics package may include a container class for
experimental data.  The class provides normal methods for computing the average,
mean, median, and other descriptive statistics that depend on the data. However,
there may be useful functions which are conceptually related but do not depend
on the data.  For instance, <code class="docutils literal notranslate"><span class="pre">erf(x)</span></code> is handy conversion routine that comes up
in statistical work but does not directly depend on a particular dataset.
It can be called either from an object or the class:  <code class="docutils literal notranslate"><span class="pre">s.erf(1.5)</span> <span class="pre">--&gt;</span> <span class="pre">.9332</span></code> or
<code class="docutils literal notranslate"><span class="pre">Sample.erf(1.5)</span> <span class="pre">--&gt;</span> <span class="pre">.9332</span></code>.</p>
<p>Since static methods return the underlying function with no changes, the
example calls are unexciting:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">E</span><span class="p">:</span>
    <span class="nd">@staticmethod</span>
    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">10</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">E</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">30</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">E</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">30</span>
</pre></div>
</div>
<p>Using the non-data descriptor protocol, a pure Python version of
<a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a> would look like this:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">StaticMethod</span><span class="p">:</span>
    <span class="s2">&quot;Emulate PyStaticMethod_Type() in Objects/funcobject.c&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span>
</pre></div>
</div>
</section>
<section id="class-methods">
<h3><a class="toc-backref" href="#id28">Class methods</a><a class="headerlink" href="#class-methods" title="Permalink to this headline">¶</a></h3>
<p>Unlike static methods, class methods prepend the class reference to the
argument list before calling the function.  This format is the same
for whether the caller is an object or a class:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">F</span><span class="p">:</span>
    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">x</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">F</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">(&#39;F&#39;, 3)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">F</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="go">(&#39;F&#39;, 3)</span>
</pre></div>
</div>
<p>This behavior is useful whenever the method only needs to have a class
reference and does not rely on data stored in a specific instance.  One use for
class methods is to create alternate class constructors.  For example, the
classmethod <a class="reference internal" href="../library/stdtypes.html#dict.fromkeys" title="dict.fromkeys"><code class="xref py py-func docutils literal notranslate"><span class="pre">dict.fromkeys()</span></code></a> creates a new dictionary from a list of
keys.  The pure Python equivalent is:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Dict</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span>
    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">fromkeys</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">iterable</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="s2">&quot;Emulate dict_fromkeys() in Objects/dictobject.c&quot;</span>
        <span class="n">d</span> <span class="o">=</span> <span class="bp">cls</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">iterable</span><span class="p">:</span>
            <span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
        <span class="k">return</span> <span class="n">d</span>
</pre></div>
</div>
<p>Now a new dictionary of unique keys can be constructed like this:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="n">Dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="s1">&#39;abracadabra&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">type</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="ow">is</span> <span class="n">Dict</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span>
<span class="go">{&#39;a&#39;: None, &#39;b&#39;: None, &#39;r&#39;: None, &#39;c&#39;: None, &#39;d&#39;: None}</span>
</pre></div>
</div>
<p>Using the non-data descriptor protocol, a pure Python version of
<a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a> would look like this:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ClassMethod</span><span class="p">:</span>
    <span class="s2">&quot;Emulate PyClassMethod_Type() in Objects/funcobject.c&quot;</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="bp">cls</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">cls</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
            <span class="bp">cls</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">),</span> <span class="s1">&#39;__get__&#39;</span><span class="p">):</span>
            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="fm">__get__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">MethodType</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">cls</span><span class="p">)</span>
</pre></div>
</div>
<p>The code path for <code class="docutils literal notranslate"><span class="pre">hasattr(type(self.f),</span> <span class="pre">'__get__')</span></code> was added in
Python 3.9 and makes it possible for <a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a> to support
chained decorators.  For example, a classmethod and property could be
chained together:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">G</span><span class="p">:</span>
    <span class="nd">@classmethod</span>
    <span class="nd">@property</span>
    <span class="k">def</span> <span class="nf">__doc__</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
        <span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;A doc for </span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s1">&#39;</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">G</span><span class="o">.</span><span class="vm">__doc__</span>
<span class="go">&quot;A doc for &#39;G&#39;&quot;</span>
</pre></div>
</div>
</section>
<section id="member-objects-and-slots">
<h3><a class="toc-backref" href="#id29">Member objects and __slots__</a><a class="headerlink" href="#member-objects-and-slots" title="Permalink to this headline">¶</a></h3>
<p>When a class defines <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>, it replaces instance dictionaries with a
fixed-length array of slot values.  From a user point of view that has
several effects:</p>
<p>1. Provides immediate detection of bugs due to misspelled attribute
assignments.  Only attribute names specified in <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> are allowed:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Vehicle</span><span class="p">:</span>
    <span class="vm">__slots__</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;id_number&#39;</span><span class="p">,</span> <span class="s1">&#39;make&#39;</span><span class="p">,</span> <span class="s1">&#39;model&#39;</span><span class="p">)</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">auto</span> <span class="o">=</span> <span class="n">Vehicle</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">auto</span><span class="o">.</span><span class="n">id_nubmer</span> <span class="o">=</span> <span class="s1">&#39;VYE483814LQEX&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;Vehicle&#39; object has no attribute &#39;id_nubmer&#39;</span>
</pre></div>
</div>
<p>2. Helps create immutable objects where descriptors manage access to private
attributes stored in <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Immutable</span><span class="p">:</span>

    <span class="vm">__slots__</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;_dept&#39;</span><span class="p">,</span> <span class="s1">&#39;_name&#39;</span><span class="p">)</span>          <span class="c1"># Replace the instance dictionary</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dept</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_dept</span> <span class="o">=</span> <span class="n">dept</span>                   <span class="c1"># Store to private attribute</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>                   <span class="c1"># Store to private attribute</span>

    <span class="nd">@property</span>                               <span class="c1"># Read-only descriptor</span>
    <span class="k">def</span> <span class="nf">dept</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dept</span>

    <span class="nd">@property</span>
    <span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>                         <span class="c1"># Read-only descriptor</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span> <span class="o">=</span> <span class="n">Immutable</span><span class="p">(</span><span class="s1">&#39;Botany&#39;</span><span class="p">,</span> <span class="s1">&#39;Mark Watney&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">dept</span>
<span class="go">&#39;Botany&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">dept</span> <span class="o">=</span> <span class="s1">&#39;Space Pirate&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">can&#39;t set attribute</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="s1">&#39;Mars&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;Immutable&#39; object has no attribute &#39;location&#39;</span>
</pre></div>
</div>
<p>3. Saves memory.  On a 64-bit Linux build, an instance with two attributes
takes 48 bytes with <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> and 152 bytes without.  This <a class="reference external" href="https://en.wikipedia.org/wiki/Flyweight_pattern">flyweight
design pattern</a> likely only
matters when a large number of instances are going to be created.</p>
<p>4. Blocks tools like <a class="reference internal" href="../library/functools.html#functools.cached_property" title="functools.cached_property"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.cached_property()</span></code></a> which require an
instance dictionary to function correctly:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">cached_property</span>

<span class="k">class</span> <span class="nc">CP</span><span class="p">:</span>
    <span class="vm">__slots__</span> <span class="o">=</span> <span class="p">()</span>                          <span class="c1"># Eliminates the instance dict</span>

    <span class="nd">@cached_property</span>                        <span class="c1"># Requires an instance dict</span>
    <span class="k">def</span> <span class="nf">pi</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="mi">4</span> <span class="o">*</span> <span class="nb">sum</span><span class="p">((</span><span class="o">-</span><span class="mf">1.0</span><span class="p">)</span><span class="o">**</span><span class="n">n</span> <span class="o">/</span> <span class="p">(</span><span class="mf">2.0</span><span class="o">*</span><span class="n">n</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span>
                       <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">100_000</span><span class="p">)))</span>
</pre></div>
</div>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">CP</span><span class="p">()</span><span class="o">.</span><span class="n">pi</span>
<span class="gt">Traceback (most recent call last):</span>
  <span class="c">...</span>
<span class="gr">TypeError</span>: <span class="n">No &#39;__dict__&#39; attribute on &#39;CP&#39; instance to cache &#39;pi&#39; property.</span>
</pre></div>
</div>
<p>It is not possible to create an exact drop-in pure Python version of
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code> because it requires direct access to C structures and control
over object memory allocation.  However, we can build a mostly faithful
simulation where the actual C structure for slots is emulated by a private
<code class="docutils literal notranslate"><span class="pre">_slotvalues</span></code> list.  Reads and writes to that private structure are managed
by member descriptors:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">null</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>

<span class="k">class</span> <span class="nc">Member</span><span class="p">:</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">offset</span><span class="p">):</span>
        <span class="s1">&#39;Emulate PyMemberDef in Include/structmember.h&#39;</span>
        <span class="c1"># Also see descr_new() in Objects/descrobject.c</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">clsname</span> <span class="o">=</span> <span class="n">clsname</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span>

    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
        <span class="s1">&#39;Emulate member_get() in Objects/descrobject.c&#39;</span>
        <span class="c1"># Also see PyMember_GetOne() in Python/structmember.c</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">null</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">value</span>

    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="s1">&#39;Emulate member_set() in Objects/descrobject.c&#39;</span>
        <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>

    <span class="k">def</span> <span class="fm">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
        <span class="s1">&#39;Emulate member_delete() in Objects/descrobject.c&#39;</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">null</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
        <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span> <span class="o">=</span> <span class="n">null</span>

    <span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="s1">&#39;Emulate member_repr() in Objects/descrobject.c&#39;</span>
        <span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;&lt;Member </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">!r}</span><span class="s1"> of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">clsname</span><span class="si">!r}</span><span class="s1">&gt;&#39;</span>
</pre></div>
</div>
<p>The <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__new__()</span></code> method takes care of adding member objects to class
variables:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Type</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
    <span class="s1">&#39;Simulate how the type metaclass adds member objects for slots&#39;</span>

    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcls</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">mapping</span><span class="p">):</span>
        <span class="s1">&#39;Emuluate type_new() in Objects/typeobject.c&#39;</span>
        <span class="c1"># type_new() calls PyTypeReady() which calls add_methods()</span>
        <span class="n">slot_names</span> <span class="o">=</span> <span class="n">mapping</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;slot_names&#39;</span><span class="p">,</span> <span class="p">[])</span>
        <span class="k">for</span> <span class="n">offset</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">slot_names</span><span class="p">):</span>
            <span class="n">mapping</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">offset</span><span class="p">)</span>
        <span class="k">return</span> <span class="nb">type</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcls</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">mapping</span><span class="p">)</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="../reference/datamodel.html#object.__new__" title="object.__new__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__new__()</span></code></a> method takes care of creating instances that have
slots instead of an instance dictionary.  Here is a rough simulation in pure
Python:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Object</span><span class="p">:</span>
    <span class="s1">&#39;Simulate how object.__new__() allocates memory for __slots__&#39;</span>

    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
        <span class="s1">&#39;Emulate object_new() in Objects/typeobject.c&#39;</span>
        <span class="n">inst</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">):</span>
            <span class="n">empty_slots</span> <span class="o">=</span> <span class="p">[</span><span class="n">null</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">)</span>
            <span class="nb">object</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="n">inst</span><span class="p">,</span> <span class="s1">&#39;_slotvalues&#39;</span><span class="p">,</span> <span class="n">empty_slots</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">inst</span>

    <span class="k">def</span> <span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="s1">&#39;Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c&#39;</span>
        <span class="bp">cls</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s1"> object has no attribute </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>

    <span class="k">def</span> <span class="fm">__delattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="s1">&#39;Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c&#39;</span>
        <span class="bp">cls</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
                <span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s1"> object has no attribute </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s1">&#39;</span>
            <span class="p">)</span>
        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__delattr__</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
<p>To use the simulation in a real class, just inherit from <code class="xref py py-class docutils literal notranslate"><span class="pre">Object</span></code> and
set the <a class="reference internal" href="../glossary.html#term-metaclass"><span class="xref std std-term">metaclass</span></a> to <code class="xref py py-class docutils literal notranslate"><span class="pre">Type</span></code>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">H</span><span class="p">(</span><span class="n">Object</span><span class="p">,</span> <span class="n">metaclass</span><span class="o">=</span><span class="n">Type</span><span class="p">):</span>
    <span class="s1">&#39;Instance variables stored in slots&#39;</span>

    <span class="n">slot_names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">]</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span>
</pre></div>
</div>
<p>At this point, the metaclass has loaded member objects for <em>x</em> and <em>y</em>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">pprint</span> <span class="kn">import</span> <span class="n">pp</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">pp</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">H</span><span class="p">)))</span>
<span class="go">{&#39;__module__&#39;: &#39;__main__&#39;,</span>
<span class="go"> &#39;__doc__&#39;: &#39;Instance variables stored in slots&#39;,</span>
<span class="go"> &#39;slot_names&#39;: [&#39;x&#39;, &#39;y&#39;],</span>
<span class="go"> &#39;__init__&#39;: &lt;function H.__init__ at 0x7fb5d302f9d0&gt;,</span>
<span class="go"> &#39;x&#39;: &lt;Member &#39;x&#39; of &#39;H&#39;&gt;,</span>
<span class="go"> &#39;y&#39;: &lt;Member &#39;y&#39; of &#39;H&#39;&gt;}</span>
</pre></div>
</div>
<p>When instances are created, they have a <code class="docutils literal notranslate"><span class="pre">slot_values</span></code> list where the
attributes are stored:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">h</span> <span class="o">=</span> <span class="n">H</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="go">{&#39;_slotvalues&#39;: [10, 20]}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">h</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">55</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="go">{&#39;_slotvalues&#39;: [55, 20]}</span>
</pre></div>
</div>
<p>Misspelled or unassigned attributes will raise an exception:</p>
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">h</span><span class="o">.</span><span class="n">xz</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;H&#39; object has no attribute &#39;xz&#39;</span>
</pre></div>
</div>
</section>
</section>
</section>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
  <h3><a href="../contents.html">Table of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">Descriptor HowTo Guide</a><ul>
<li><a class="reference internal" href="#primer">Primer</a><ul>
<li><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant">Simple example: A descriptor that returns a constant</a></li>
<li><a class="reference internal" href="#dynamic-lookups">Dynamic lookups</a></li>
<li><a class="reference internal" href="#managed-attributes">Managed attributes</a></li>
<li><a class="reference internal" href="#customized-names">Customized names</a></li>
<li><a class="reference internal" href="#closing-thoughts">Closing thoughts</a></li>
</ul>
</li>
<li><a class="reference internal" href="#complete-practical-example">Complete Practical Example</a><ul>
<li><a class="reference internal" href="#validator-class">Validator class</a></li>
<li><a class="reference internal" href="#custom-validators">Custom validators</a></li>
<li><a class="reference internal" href="#practical-application">Practical application</a></li>
</ul>
</li>
<li><a class="reference internal" href="#technical-tutorial">Technical Tutorial</a><ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#definition-and-introduction">Definition and introduction</a></li>
<li><a class="reference internal" href="#descriptor-protocol">Descriptor protocol</a></li>
<li><a class="reference internal" href="#overview-of-descriptor-invocation">Overview of descriptor invocation</a></li>
<li><a class="reference internal" href="#invocation-from-an-instance">Invocation from an instance</a></li>
<li><a class="reference internal" href="#invocation-from-a-class">Invocation from a class</a></li>
<li><a class="reference internal" href="#invocation-from-super">Invocation from super</a></li>
<li><a class="reference internal" href="#summary-of-invocation-logic">Summary of invocation logic</a></li>
<li><a class="reference internal" href="#automatic-name-notification">Automatic name notification</a></li>
<li><a class="reference internal" href="#orm-example">ORM example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#pure-python-equivalents">Pure Python Equivalents</a><ul>
<li><a class="reference internal" href="#properties">Properties</a></li>
<li><a class="reference internal" href="#functions-and-methods">Functions and methods</a></li>
<li><a class="reference internal" href="#kinds-of-methods">Kinds of methods</a></li>
<li><a class="reference internal" href="#static-methods">Static methods</a></li>
<li><a class="reference internal" href="#class-methods">Class methods</a></li>
<li><a class="reference internal" href="#member-objects-and-slots">Member objects and __slots__</a></li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>Previous topic</h4>
  <p class="topless"><a href="curses.html"
                        title="previous chapter">Curses Programming with Python</a></p>
  <h4>Next topic</h4>
  <p class="topless"><a href="functional.html"
                        title="next chapter">Functional Programming HOWTO</a></p>
  <div role="note" aria-label="source link">
    <h3>This Page</h3>
    <ul class="this-page-menu">
      <li><a href="../bugs.html">Report a Bug</a></li>
      <li>
        <a href="https://github.com/python/cpython/blob/3.9/Doc/howto/descriptor.rst"
            rel="nofollow">Show Source
        </a>
      </li>
    </ul>
  </div>
        </div>
      </div>
      <div class="clearer"></div>
    </div>  
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="../py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="functional.html" title="Functional Programming HOWTO"
             >next</a> |</li>
        <li class="right" >
          <a href="curses.html" title="Curses Programming with Python"
             >previous</a> |</li>

    <li><img src="../_static/py.png" alt=""
             style="vertical-align: middle; margin-top: -1px"/></li>
    <li><a href="https://www.python.org/">Python</a> &#187;</li>
    

    <li>
      <a href="../index.html">3.9.6 Documentation</a> &#187;
    </li>

          <li class="nav-item nav-item-1"><a href="index.html" >Python HOWTOs</a> &#187;</li>
    <li class="right">
        

    <div class="inline-search" style="display: none" role="search">
        <form class="inline-search" action="../search.html" method="get">
          <input placeholder="Quick search" type="text" name="q" />
          <input type="submit" value="Go" />
          <input type="hidden" name="check_keywords" value="yes" />
          <input type="hidden" name="area" value="default" />
        </form>
    </div>
    <script type="text/javascript">$('.inline-search').show(0);</script>
         |
    </li>

      </ul>
    </div>  
    <div class="footer">
    &copy; <a href="../copyright.html">Copyright</a> 2001-2021, Python Software Foundation.
    <br />

    The Python Software Foundation is a non-profit corporation.
<a href="https://www.python.org/psf/donations/">Please donate.</a>
<br />
    <br />

    Last updated on Jun 28, 2021.
    <a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
    <br />

    Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 2.3.1.
    </div>

  </body>
</html>